/*
 * Copyright 2019 xiaomaoguai.com All right reserved. This software is the
 * confidential and proprietary information of xiaomaoguai.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with xiaomaoguai.com.
 */

package com.xiaomaoguai.core.next.hsf.http;

import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Set;

/**
 * HSF接口扫描工具类，扫描 package 并注册 {@link HsfHttpRequestFactoryBean}
 *
 * @author chenyao
 * @since 2016年8月24日 下午2:35:56
 * @see HsfHttpRequestFactoryBean
 * @see HsfHttpRequestScannerConfigurer
 */
public class ClassPathHsfInterfaceScanner extends ClassPathBeanDefinitionScanner {
    private Class<? extends Annotation> annotationClass;
    private Class<?>                    markerInterface;
    private HsfHttpRequestProxy         hsfHttpRequestProxy;
    private String                      hsfHttpRequestProxyBeanName;
    private RestTemplate                restTemplate;
    private String                      restTemplateBeanName;
    private String                      serviceVersion;
    private String                      serverUrl;
    private ObjectDeserializer          responseBodyDeserializer;
    private String                      responseBodyDeserializerBeanName;

    /**
     * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean
     * factory
     *
     * @param registry the {@code BeanFactory} to load bean definitions into, in
     *            the form * of a {@code BeanDefinitionRegistry}
     */
    public ClassPathHsfInterfaceScanner(BeanDefinitionRegistry registry) {
        this(registry, true);
    }

    /**
     * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean
     * factory
     *
     * @param registry the {@code BeanFactory} to load bean definitions into, in
     *            the form of a {@code BeanDefinitionRegistry}
     * @param useDefaultFilters whether to include the default filters for the
     */
    public ClassPathHsfInterfaceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    /**
     * Create a new {@code ClassPathBeanDefinitionScanner} for the given bean
     * factory
     *
     * @param registry the {@code BeanFactory} to load bean definitions into, in
     *            the form * of a {@code BeanDefinitionRegistry}
     * @param useDefaultFilters whether to include the default filters for the
     * @param environment the Spring {@link Environment} to use when evaluating
     *            bean definition profile metadata
     */
    public ClassPathHsfInterfaceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                        Environment environment) {
        super(registry, useDefaultFilters, environment);
    }

    /**
     * Configures parent scanner to search for the right interfaces. It can
     * search for all interfaces or just for those that extends a
     * markerInterface or/and those annotated with the annotationClass
     */
    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        // if specified, use the given annotation and / or marker interface
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        // override AssignableTypeFilter to ignore matches on the actual marker interface
        if (this.markerInterface != null) {
            addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
                @Override
                protected boolean matchClassName(String className) {
                    return false;
                }
            });
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter(new TypeFilter() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                        throws IOException {
                    return true;
                }
            });
        }

        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                    throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                return className.endsWith("package-info");
            }
        });
    }

    /**
     * 根据<code>basePackages</code>扫描包下的HSF interface
     *
     * @param basePackages 需要扫描的package名字
     */
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (beanDefinitions.isEmpty()) {
            logger.warn("No HSF interface was found in '" + Arrays.toString(basePackages)
                    + "' package. Please check your configuration.");
        } else {
            this.serviceVersion = resolvePlaceholders(this.serviceVersion);
            this.serverUrl = resolvePlaceholders(this.serverUrl);
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    /**
     * Resolve ${...} placeholders in the given text
     *
     * @param text the String to resolve
     * @return the resolved String (never {@code null})
     */
    public String resolvePlaceholders(String text) {
        if (StringUtils.isEmpty(text)) {
            return text;
        } else {
            ResourceLoader resourceLoader = this.getResourceLoader();
            return ((GenericApplicationContext) resourceLoader).getEnvironment().resolvePlaceholders(text);
        }
    }

    /**
     * 设置<code>HsfHttpRequestFactoryBean</code>的属性
     *
     * @param beanDefinitions Holder for a BeanDefinition with name and aliases
     */
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        for (BeanDefinitionHolder holder : beanDefinitions) {
            GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

            if (logger.isDebugEnabled()) {
                logger.debug("Creating HsfHttpRequestFactoryBean with name '" + holder.getBeanName() + "' and '"
                        + definition.getBeanClassName() + "' hsfInterface");
            }

            // the HSF interface is the original class of the bean
            // but, the actual class of the bean is HsfHttpRequestFactoryBean
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            definition.setBeanClass(HsfHttpRequestFactoryBean.class);

            // 设置FactoryBean的属性
            if (this.hsfHttpRequestProxy != null) {
                definition.getPropertyValues().add("hsfHttpRequestProxy", this.hsfHttpRequestProxy);
            } else if (StringUtils.hasText(this.hsfHttpRequestProxyBeanName)) {
                definition.getPropertyValues()
                        .add("hsfHttpRequestProxy", new RuntimeBeanReference(this.hsfHttpRequestProxyBeanName));
            }
            if (this.restTemplate != null) {
                definition.getPropertyValues().add("restTemplate", this.restTemplate);
            } else if (StringUtils.hasText(this.restTemplateBeanName)) {
                definition.getPropertyValues().add("restTemplate", new RuntimeBeanReference(this.restTemplateBeanName));
            }
            definition.getPropertyValues().add("serviceVersion", this.serviceVersion);
            definition.getPropertyValues().add("serverUrl", this.serverUrl);
            if (this.responseBodyDeserializer != null) {
                definition.getPropertyValues().add("responseBodyDeserializer", this.responseBodyDeserializer);
            } else if (StringUtils.hasText(this.responseBodyDeserializerBeanName)) {
                definition.getPropertyValues()
                        .add("responseBodyDeserializer",
                                new RuntimeBeanReference(this.responseBodyDeserializerBeanName));
            }
        }
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

    /**
     * 使用注解过滤要扫描的class
     *
     * @param annotationClass 使用注解过滤要扫描的class
     * @return {@code ClassPathHsfInterfaceScanner}
     */
    public ClassPathHsfInterfaceScanner setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
        return this;
    }

    /**
     * 只注册继承自markerInterface的接口
     *
     * @param markerInterface 只注册继承自markerInterface的接口
     * @return {@code ClassPathHsfInterfaceScanner}
     */
    public ClassPathHsfInterfaceScanner setMarkerInterface(Class<?> markerInterface) {
        this.markerInterface = markerInterface;
        return this;
    }

    /**
     * HSF接口使用HTTP请求代理类，如果没有设置此值，下面这三个字段必须设置：
     * <ul>
     * <li>{@link #setRestTemplate(RestTemplate)}（可选）
     * <li>{@link #setServiceVersion(String)}
     * <li>{@link #setServerUrl(String)}
     * </ul>
     *
     * @param hsfHttpRequestProxy HSF接口使用HTTP请求代理类
     * @return {@code ClassPathHsfInterfaceScanner}
     */
    public ClassPathHsfInterfaceScanner setHsfHttpRequestProxy(HsfHttpRequestProxy hsfHttpRequestProxy) {
        this.hsfHttpRequestProxy = hsfHttpRequestProxy;
        return this;
    }

    /**
     * 设置 {@link #setHsfHttpRequestProxy(HsfHttpRequestProxy)} 属性对象的Bean name
     *
     * @param hsfHttpRequestProxyBeanName hsfHttpRequestProxy对象的Bean name
     * @return {@code ClassPathHsfInterfaceScanner}
     */
    public ClassPathHsfInterfaceScanner setHsfHttpRequestProxyBeanName(String hsfHttpRequestProxyBeanName) {
        this.hsfHttpRequestProxyBeanName = hsfHttpRequestProxyBeanName;
        return this;
    }

    /**
     * 访问Rest服务的客户端，建议此字段设置成具有连接池的单一对象
     *
     * @param restTemplate 访问Rest服务的客户端
     * @return {@code ClassPathHsfInterfaceScanner}
     * @see #setHsfHttpRequestProxy(HsfHttpRequestProxy)
     */
    public ClassPathHsfInterfaceScanner setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        return this;
    }

    /**
     * 设置 {@link #setRestTemplate(RestTemplate)} 属性对象的Bean name
     *
     * @param restTemplateBeanName restTemplate属性对象的Bean name
     * @return {@code ClassPathHsfInterfaceScanner}
     */
    public ClassPathHsfInterfaceScanner setRestTemplateBeanName(String restTemplateBeanName) {
        this.restTemplateBeanName = restTemplateBeanName;
        return this;
    }

    /**
     * HSF接口的服务版本，举例：1.0.0，此字段可以使用通配符"${}"获取 {@link Environment} 中的属性
     *
     * @param serviceVersion HSF接口的服务版本
     * @return {@code ClassPathHsfInterfaceScanner}
     * @see #setHsfHttpRequestProxy(HsfHttpRequestProxy)
     */
    public ClassPathHsfInterfaceScanner setServiceVersion(String serviceVersion) {
        this.serviceVersion = serviceVersion;
        return this;
    }

    /**
     * HSF服务的HTTP IP地址，举例：http://ip:port，此字段可以使用通配符"${}"获取 {@link Environment}
     * 中的属性
     *
     * @param serverUrl HSF服务的HTTP IP地址
     * @return {@code ClassPathHsfInterfaceScanner}
     * @see #setHsfHttpRequestProxy(HsfHttpRequestProxy)
     */
    public ClassPathHsfInterfaceScanner setServerUrl(String serverUrl) {
        this.serverUrl = serverUrl;
        return this;
    }

    /**
     * 使用自定义反序列化类来操作：HSF接口返回的值，解决某些泛型类型、特殊的需求（可选）
     *
     * @param responseBodyDeserializer 使用自定义反序列化类
     * @see #setHsfHttpRequestProxy(HsfHttpRequestProxy)
     */
    public void setResponseBodyDeserializer(ObjectDeserializer responseBodyDeserializer) {
        this.responseBodyDeserializer = responseBodyDeserializer;
    }

    /**
     * 设置 {@link #setResponseBodyDeserializer(ObjectDeserializer)} 属性对象的Bean
     * name
     *
     * @param responseBodyDeserializerBeanName responseBodyDeserializer属性对象的Bean
     */
    public void setResponseBodyDeserializerBeanName(String responseBodyDeserializerBeanName) {
        this.responseBodyDeserializerBeanName = responseBodyDeserializerBeanName;
    }
}
